package com.kmxd.ams.app.use;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.kmxd.ams.client.arrange.IDocumentSvc;
import com.kmxd.ams.client.arrange.IVolumeSvc;
import com.kmxd.ams.client.search.vo.SearchAuthVO;
import com.kmxd.ams.client.system.ISysDeptSvc;
import com.kmxd.ams.client.system.ISysUserSvc;
import com.kmxd.ams.client.use.IUsePushDetailSvc;
import com.kmxd.ams.client.use.IUsePushSvc;
import com.kmxd.ams.client.use.IUsePushUserSvc;
import com.kmxd.ams.client.use.dto.*;
import com.kmxd.ams.client.use.vo.UsePushDetailListVO;
import com.kmxd.ams.client.use.vo.UsePushListVO;
import com.kmxd.ams.client.use.vo.UsePushUserListVO;
import com.kmxd.ams.client.use.vo.UsePushVO;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.UsePushStatusEnum;
import com.kmxd.ams.core.common.enums.UsePushTypeEnum;
import com.kmxd.ams.core.common.query.QueryGen;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.system.entity.SysDept;
import com.kmxd.ams.infra.use.entity.UsePush;
import com.kmxd.ams.infra.use.entity.UsePushDetail;
import com.kmxd.ams.infra.use.entity.UsePushUser;
import com.kmxd.ams.infra.use.mapper.UsePushMapper;
import java.util.*;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 推送表 服务实现类
 *
 * @author nickbi
 * @since 2023-01-11
 */
@Service
@AllArgsConstructor
public class UsePushSvcImpl extends ServiceImpl<UsePushMapper, UsePush> implements IUsePushSvc {

  private final IUsePushUserSvc usePushUserSvc;
  private final IUsePushDetailSvc usePushDetailSvc;
  private final ISysUserSvc sysUserSvc;
  private final IDocumentSvc documentSvc;
  private final IVolumeSvc volumeSvc;
  private final ISysDeptSvc sysDeptSvc;
  private UsePushMapper usePushMapper;

  @Override
  public Page<UsePushListVO> selectPage(Page reqPage, UsePushQueryDTO req) {
    LambdaQueryWrapper<UsePush> queryWrapper =
        QueryGen.init(new QueryWrapper<UsePush>(), req).lambda();
    queryWrapper.eq(UsePush::getComId, SecurityUtil.getCropId());
    queryWrapper.eq(UsePush::getFondsId, SecurityUtil.getFondsId());
    queryWrapper.orderByDesc(UsePush::getCreatedTime);
    UsePushTypeEnum type = UsePushTypeEnum.getValue(req.getType());
    queryWrapper.eq(UsePush::getType, req.getType());
    if (ObjectUtil.equals(UsePushTypeEnum.ACTIVE_PUSH, type)) {
      queryWrapper.eq(UsePush::getPushUserId, SecurityUtil.getUserId());
    }
    Page<UsePush> page = usePushMapper.selectPage(reqPage, queryWrapper);
    Page<UsePushListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), UsePushListVO.class));
    return voPage;
  }

  @Override
  public List<UsePushListVO> selectList(UsePushQueryDTO req) {
    LambdaQueryWrapper<UsePush> queryWrapper =
        QueryGen.init(new QueryWrapper<UsePush>(), req).lambda();
    queryWrapper.eq(UsePush::getComId, SecurityUtil.getCropId());
    queryWrapper.eq(UsePush::getFondsId, SecurityUtil.getFondsId());
    queryWrapper.orderByDesc(UsePush::getCreatedTime);
    UsePushTypeEnum type = UsePushTypeEnum.getValue(req.getType());
    if (ObjectUtil.equals(UsePushTypeEnum.ACTIVE_PUSH, type)) {
      queryWrapper.eq(UsePush::getPushUserId, SecurityUtil.getUserId());
    }
    List<UsePush> list = usePushMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, UsePushListVO.class);
  }

  @Override
  public Page<UsePushListVO> applyPage(Page reqPage, UsePushQueryDTO req) {
    LambdaQueryWrapper<UsePush> queryWrapper =
        QueryGen.init(new QueryWrapper<UsePush>(), req).lambda();
    queryWrapper.eq(UsePush::getComId, SecurityUtil.getCropId());
    queryWrapper.eq(UsePush::getFondsId, SecurityUtil.getFondsId());
    queryWrapper.eq(UsePush::getType, UsePushTypeEnum.APPLY.getCode());
    queryWrapper.eq(UsePush::getUserId, SecurityUtil.getUserId());
    Page<UsePush> page = usePushMapper.selectPage(reqPage, queryWrapper);
    Page<UsePushListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    voPage.setRecords(BeanUtil.copyToList(page.getRecords(), UsePushListVO.class));
    return voPage;
  }

  @Override
  public Page<UsePushListVO> pushPage(Page reqPage, UsePushQueryDTO req) {
    req.setComId(SecurityUtil.getCropId());
    req.setFondsId(SecurityUtil.getFondsId());
    req.setUserId(SecurityUtil.getUserId());
    req.setDeptId(SecurityUtil.getLoginUser().getDeptId());
    req.setStatus(UsePushStatusEnum.PUSHED.getCode());
    List<UsePushListVO> page =
        usePushMapper.selectPushPage(req, reqPage.offset(), reqPage.getSize());
    Long total = usePushMapper.selectPushPageCount(req);
    Page<UsePushListVO> res = new Page<>(reqPage.getCurrent(), reqPage.getSize());
    res.setRecords(page);
    res.setTotal(total);
    return res;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(UsePushAddDTO req) {
    UsePush entity = BeanUtil.copyProperties(req, UsePush.class);
    entity.setComId(SecurityUtil.getCropId());
    entity.setFondsId(SecurityUtil.getFondsId());
    entity.setType(req.getType().getCode());
    entity.setId(IdWorker.getId());
    if (ObjectUtil.isNull(req.getStartTime())) {
      entity.setStartTime(new Date());
    }
    entity.setCreatedBy(SecurityUtil.getUserId());
    entity.setCreatedTime(new Date());
    entity.setEndTime(req.getEndTime());
    List<UsePushUserAddDTO> userList =
        Optional.ofNullable(req.getUserList()).orElse(Lists.newArrayList());
    if (ObjectUtil.equals(UsePushTypeEnum.APPLY, req.getType())) {
      entity.setUserId(SecurityUtil.getUserId());
      entity.setUsername(SecurityUtil.getRealname());
      entity.setDeptId(SecurityUtil.getLoginUser().getDeptId());
      entity.setDeptName(SecurityUtil.getLoginUser().getDeptName());
      UsePushUserAddDTO userAddDTO = new UsePushUserAddDTO();
      userAddDTO.setUserId(SecurityUtil.getUserId());
      userAddDTO.setUsername(SecurityUtil.getRealname());
      userAddDTO.setDeptId(SecurityUtil.getLoginUser().getDeptId());
      userAddDTO.setDeptName(SecurityUtil.getLoginUser().getDeptName());
      userList.add(userAddDTO);
    } else {
      entity.setPushUserId(SecurityUtil.getUserId());
      entity.setPushUsername(SecurityUtil.getRealname());
      entity.setPushDeptId(SecurityUtil.getLoginUser().getDeptId());
      entity.setPushDeptName(SecurityUtil.getLoginUser().getDeptName());
    }
    // 添加用户
    if (ObjectUtil.isEmpty(userList)) {
      throw BizTipException.instance(ErrorCode.VALIDATE_NOT_NULL, "推送用户不能为空");
    }
    List<UsePushUser> pushUserList = buildUserPushUserList(userList, entity);
    usePushUserSvc.saveBatch(pushUserList);
    // 添加档案

    if (ObjectUtil.equals(UsePushTypeEnum.ACTIVE_PUSH, req.getType())
        && ObjectUtil.isEmpty(req.getArchList())) {
      throw BizTipException.instance(ErrorCode.VALIDATE_NOT_NULL, "推送档案不能为空");
    }
    if (ObjectUtil.isNotEmpty(req.getArchList())) {
      req.getArchList()
          .forEach(
              arch -> {
                arch.setPushId(entity.getId());
              });
      usePushDetailSvc.saveBatch(req.getArchList());
    }
    entity.setOnlineBrowser(req.getOnlineBrowser());
    entity.setBrowserFile(req.getBrowserFile());
    entity.setExcerpt(req.getExcerpt());
    entity.setOnlinePrint(req.getOnlinePrint());
    entity.setDownload(req.getDownload());
    entity.setDownloadSource(req.getDownloadSource());
    usePushMapper.insert(entity);
  }

  private List<UsePushUser> buildUserPushUserList(
      List<UsePushUserAddDTO> userList, UsePush entity) {
    List<SysDept> deptList =
        sysDeptSvc.listByIds(
            userList.stream().map(UsePushUserAddDTO::getDeptId).collect(Collectors.toList()));
    Map<Long, String> deptNameMap =
        deptList.stream().collect(Collectors.toMap(SysDept::getId, SysDept::getName));
    List<UsePushUser> userAddDTOS =
        userList.stream()
            .map(
                user -> {
                  UsePushUser dto = new UsePushUser();
                  dto.setId(null);
                  dto.setPushId(entity.getId());
                  dto.setUserId(user.getUserId());
                  dto.setUsername(user.getUsername());
                  dto.setDeptId(user.getDeptId());
                  if (deptNameMap.containsKey(user.getDeptId())) {
                    String deptName = deptNameMap.get(user.getDeptId());
                    dto.setDeptName(deptName);
                  }
                  return dto;
                })
            .collect(Collectors.toList());
    return userAddDTOS;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void edit(UsePushEditDTO req) {
    UsePush entity = BeanUtil.copyProperties(req, UsePush.class);
    if (ObjectUtil.equals(UsePushTypeEnum.APPLY, req.getType())) {
      entity = usePushMapper.selectById(req.getId());
      entity.setRemarks(req.getRemarks());
      entity.setPushUserId(SecurityUtil.getUserId());
      entity.setPushUsername(SecurityUtil.getRealname());
      entity.setPushDeptId(SecurityUtil.getLoginUser().getDeptId());
      entity.setPushDeptName(SecurityUtil.getLoginUser().getDeptName());
    }
    entity.setType(req.getType().getCode());
    entity.setOnlineBrowser(req.getOnlineBrowser());
    entity.setBrowserFile(req.getBrowserFile());
    entity.setExcerpt(req.getExcerpt());
    entity.setOnlinePrint(req.getOnlinePrint());
    entity.setDownload(req.getDownload());
    entity.setDownloadSource(req.getDownloadSource());
    entity.setEndTime(req.getEndTime());
    usePushMapper.updateById(entity);
    final Long id = entity.getId();
    // 添加用户
    if (ObjectUtil.isNotEmpty(req.getUserList())) {
      List<UsePushUser> userList = buildUserPushUserList(req.getUserList(), entity);
      usePushUserSvc.remove(
          Wrappers.<UsePushUser>lambdaQuery().eq(UsePushUser::getPushId, entity.getId()));
      usePushUserSvc.saveBatch(userList);
    }

    // 添加档案

    if (ObjectUtil.isNotEmpty(req.getArchList())) {
      req.getArchList()
          .forEach(
              arch -> {
                arch.setPushId(id);
                arch.setId(null);
              });
      usePushDetailSvc.remove(
          Wrappers.<UsePushDetail>lambdaQuery().eq(UsePushDetail::getPushId, entity.getId()));
      usePushDetailSvc.saveBatch(req.getArchList());
    }
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    this.baseMapper.deleteBatchIds(idList);
  }

  @Override
  public UsePushVO view(Long id) {
    UsePush entity =
        Optional.ofNullable(usePushMapper.selectById(id))
            .orElseThrow(() -> BizTipException.instance(ErrorCode.USE_PUSH_IS_NULL, "未找到推送信息"));
    UsePushVO vo = BeanUtil.copyProperties(entity, UsePushVO.class);
    UsePushDetailQueryDTO qry = new UsePushDetailQueryDTO();
    qry.setPushId(entity.getId());
    List<UsePushDetailListVO> detailListVOS = usePushDetailSvc.selectList(qry);
    vo.setArchList(detailListVOS);
    UsePushUserQueryDTO qryUser = new UsePushUserQueryDTO();
    qryUser.setPushId(entity.getId());
    List<UsePushUserListVO> userList = usePushUserSvc.selectList(qryUser);
    vo.setUserList(userList);
    return vo;
  }

  @Override
  public UsePushVO viewPush(Long id, Long userId) {
    UsePush entity =
        Optional.ofNullable(
                usePushMapper.selectOne(Wrappers.<UsePush>lambdaQuery().eq(UsePush::getId, id)))
            .orElseThrow(() -> BizTipException.instance(ErrorCode.USE_PUSH_IS_NULL, "未找到推送信息"));
    List<UsePushUser> list =
        usePushUserSvc.list(
            Wrappers.<UsePushUser>lambdaQuery()
                .eq(UsePushUser::getPushId, entity.getId())
                .eq(UsePushUser::getUserId, userId));
    if (ObjectUtil.isEmpty(list)) {
      BizTipException.instance(ErrorCode.USE_PUSH_IS_NULL, "未找到推送信息");
    }

    return BeanUtil.copyProperties(entity, UsePushVO.class);
  }

  /**
   * 获取推送权限
   *
   * @param req
   * @return
   */
  @Override
  public SearchAuthVO getAuth(UsePushAuthQueryDTO req) {

    SearchAuthVO searchAuthVO = new SearchAuthVO();
    if (ObjectUtil.isNull(req.getCurrentTime())) {
      req.setCurrentTime(new Date());
    }
    List<UsePushListVO> usePushListList = usePushMapper.selectHaveAuthList(req);
    if (ObjectUtil.isEmpty(usePushListList)) {
      return searchAuthVO;
    }
    usePushListList.forEach(
        pushVO -> {
          if (BooleanUtil.isTrue(pushVO.getOnlineBrowser())) {
            searchAuthVO.setOnlinePrint(pushVO.getOnlineBrowser());
          }
          if (BooleanUtil.isTrue(pushVO.getBrowserFile())) {
            searchAuthVO.setBrowserFile(pushVO.getBrowserFile());
          }
          if (BooleanUtil.isTrue(pushVO.getExcerpt())) {
            searchAuthVO.setExcerpt(pushVO.getExcerpt());
          }
          if (BooleanUtil.isTrue(pushVO.getOnlinePrint())) {
            searchAuthVO.setOnlinePrint(pushVO.getOnlinePrint());
          }
          if (BooleanUtil.isTrue(pushVO.getDownload())) {
            searchAuthVO.setDownload(pushVO.getDownload());
          }
          if (BooleanUtil.isTrue(pushVO.getDownloadSource())) {
            searchAuthVO.setDownloadSource(pushVO.getDownloadSource());
          }
        });

    return searchAuthVO;
  }

  @Override
  public void operate(UsePushOperateDTO req) {
    UsePush usePush = usePushMapper.selectById(req.getId());
    if (StrUtil.isNotBlank(req.getRemarks())) {
      usePush.setRemarks(req.getRemarks());
    }
    switch (req.getType()) {
      case PUSH:
        long count =
            usePushDetailSvc.count(
                Wrappers.<UsePushDetail>lambdaQuery()
                    .eq(UsePushDetail::getPushId, usePush.getId()));
        if (count < 1) {
          throw BizTipException.instance(ErrorCode.USE_PUSH_ARCH_IS_NULL, "档案不能为空");
        }
        usePush.setStatus(UsePushStatusEnum.PUSHED.getCode());
        usePush.setPushTime(new Date());
        break;
      case CANCEL_PUSH:
        usePush.setStatus(UsePushStatusEnum.CANCEL.getCode());
        break;
      case REFUSE:
        usePush.setStatus(UsePushStatusEnum.REFUSED.getCode());
        break;
      default:
        break;
    }
    usePushMapper.updateById(usePush);
  }
}
